Copyright (c) 1991-1993 Borland International, Inc. All Rights Reserved. THE BLADERUNNER UI LANGUAGE EXTENSIONS -------------------------------------- Bladerunner extends the dBASE language with new commands and functions for creating graphical, event-driven user interfaces. For compatibility with DOS applications, Bladerunner still supports the dBASE IV windowing and menu mechanisms. However, to take full advantage of the Windows environment, you need to understand and use the new extensions. This document introduces the new UI extensions and describes the steps for creating and using windows. Contents -------- I. Overview II. Summary of Steps III. Step 1 - Define the window IV. Step 2 - Define controls V. Step 3 - Assign actions VI. Step 4 - Read the window VII. Upgrading dBASE DOS screen forms Overview -------- In a Bladerunner application, the window is the primary user interface container. Windows can contain controls, such as entry fields and list boxes, for interacting with the user and can also contain other windows. When you design the user interface for your application, you are essentially designing its windows. The Bladerunner mechanism for working with windows is familiar to dBASE IV programmers, with these important differences: - In dBASE IV, there is an active state for GETs (READ) and an active state for menus (ACTIVATE MENU/POPUP); both can be visible, but only one at a time is active. In Bladerunner, there is a single active state for a window. Use READMODAL() or OPEN WINDOW to activate GETs (called entry fields), menus, and any other controls in the window. - In dBASE IV, only one window at a time can be active. In Bladerunner, several windows can be active at once. A new command, OPEN WINDOW, displays a window and continues program execution, letting you display more windows, or execute other code, without stopping execution to get user responses. - In dBASE IV, you DEFINE a window, ACTIVATE it, display your SAYs and GETs, then READ. In Bladerunner, you don't ACTIVATE windows; instead, you DEFINE the window, DEFINE controls OF the window, then READMODAL() or OPEN WINDOW. The next section describes these steps in more detail. Summary of Steps ---------------- Below is a summary of the steps for creating and reading a Bladerunner window. A detailed explanation of each step follows the summary. 1. Define the window with DEFINE WINDOW 2. Define controls for the window with DEFINE OF 3. Assign actions with ON SELECTION WINDOW ON SELECTION PULLDOWN 4. Read the window with READMODAL() or OPEN WINDOW Step 1 - Define the Window -------------------------- DEFINE WINDOW has several options for specifying the look and behavior of the window. Using these options in different combinations, you can define input windows, dialog boxes or any other kind of window. Type HELP DEFINE WINDOW from the Command Window for a list of options. The Default Font and the Coordinate Plane Each window has a default font which Bladerunner uses to display any controls or output that don't specify a font. Use the FONT option of DEFINE WINDOW to set the window's default font. If you don't use the FONT option, the default font is the Windows system font. A window's default font is especially important because it defines the size of the coordinate plane for placing controls in the window. That is, the row height is determined by the line height of the default font, and the column width is determined by the character width of the default font. So, when you issue this command: DEFINE TEXT FNHeading OF MyWindow AT 6,16 PROMPT "First Name" Bladerunner displays "First Name" at row 6 (six lines down in the default font), column 16 (sixteen characters across in the default font). Proportional fonts, such as Helvetica or Times, have varied widths for each character: an "i" takes up less space than a "w." If you specify a proportional font as the default font, Bladerunner uses the average character width as the column width. For this reason, using a non-proportional font as the default window font makes working with window coordinates a little easier. With a non-proportional default font, you can still specify proportional fonts for the controls that contain text. "Specifying Coordinates" in Step 2 describes two new functions, NEXTROW() and NEXTCOL(), for displaying controls with varied fonts. Dialog Boxes A dialog box is a window you create for getting information from the user. The DLGFRAME option of DEFINE WINDOW makes the window's border look like a standard Windows dialog box, and makes the window non-sizeable. These other options of DEFINE WINDOW are useful for creating dialog boxes: NOMAXIMIZE Displays the window without a Maximize button. NOMINIMIZE Displays the window without a Minimize button. AUTOSIZE Automatically sets the lower right coordinate of the window to contain the lowest and rightmost control in the window. This example defines a dialog box for prompting the user to enter a customer name: DEFINE WINDOW MyDialog AT 10,10 OF DESKTOP DLGFRAME NOMAXIMIZE; NOMINIMIZE AUTOSIZE MESSAGE "Enter a customer name" cName = SPACE(30) DEFINE TEXT Head1 AT 2,2 OF MyDialog PROMPT "Customer name:" DEFINE ENTRYFIELD GetName WITH cName at 2,NEXTCOL() OF MyDialog DEFINE PUSHBUTTON Ok AT 4,5 OF MyDialog PROMPT "OK" DEFINE PUSHBUTTON Cancel AT 4,NEXTCOL() OF MyDialog ; PROMPT "Cancel" Step 2 - Define controls ------------------------ A control is an object you place in a window for interacting with the user. Bladerunner provides several controls for displaying and editing data, such as list boxes and radio buttons, and controls for getting user responses, such as pull-down menus and pushbuttons. You place a control in a window by defining the control. Here is the basic syntax: DEFINE OF AT The following examples define controls for MyWindow: DEFINE TEXT Get1Title OF MyWindow AT 1,5 PROMPT "Enter a name" DEFINE ENTRYFIELD Get1 WITH mName OF MyWindow AT 1,20 DEFINE PUSHBUTTON Push1 OF MyWindow AT 3,2 PROMPT "OK" DEFINE LISTBOX List1 OF MyWindow FROM 3,10 TO 9,25 PROMPT FILES Specifying coordinates For precise placement of controls, you can specify coordinates with decimal values. For example: DEFINE PUSHBUTTON OK AT 3.5, 1.5 OF MyWindow PROMPT "OK" Controls that contain text, such as entry fields and radiobuttons, have a FONT option. Even though each control you place may specify a different font, the coordinates for placing controls are always based on the window's default font. When positioning controls with different fonts, the relative addressing technique commonly used in dBASE DOS, using ROW() and COL(), can misalign controls. For instance: @ 1,2 SAY "First line" @ ROW()+1,2 SAY "Second line" @ ROW()+1,2 SAY "Third line" will display correctly aligned only if all three objects and the window in which they are displayed use the same font. To accomodate relative addressing with varied fonts, Bladerunner provides NEXTROW() and NEXTCOL(). NEXTROW() returns the next available row on the coordinate plane given the last control drawn; NEXTCOL() returns the next available column. For instance DEFINE FONT Times10 ........ @ 2,2 SAY "First line" FONT Times10 @ NEXTROW(),2 SAY "Second line" FONT Arial24 @ NEXTROW(),2 SAY "Third line" FONT Script48 @ ROW(),NEXTCOL() SAY "Second SAY on Third line" FONT Times10 will display correctly aligned regardless of the fonts you specify. Data Controls These controls display and edit data. ENTRYFIELDs correspond to GETs, and TEXT controls correspond to SAYs. DEFINE... So a user can --------- ------------- ENTRYFIELD Enter a single value COMBOBOX Enter or select one of any number of values SPINBOX Enter or select one in an ordered set of values SCROLLBAR Select one in an ordered set of values CHECKBOX Select one of two logical values RADIOBUTTONS Select one of a few values LISTBOX Select one or more of any number of values BROWSE View or edit fields in a table - tabular view EDIT View or edit fields in a table - record view EDITOR View or edit text BOX See a box TEXT See text as a heading, desc., or instruction. GRAPH See and interact with a graph of table data IMAGE See a bitmapped image Action controls These controls, menus, speedbars, and pushbuttons, cause actions to occur. Bladerunner supports dBASE IV's horizontal bar menus (created with DEFINE MENU and DEFINE PAD) and popup menus (created with DEFINE POPUP and DEFINE BAR) only for dBASE IV compatibility. To create menus in Bladerunner, use these new commands: DEFINE MENU CHOICE Defines an item on the menu bar DEFINE PULLDOWN Defines a pulldown menu DEFINE PULLDOWN CHOICE Defines an item on a pulldown menu ON MENU CHOICE Assigns a pulldown menu to a menu bar item A Bladerunner window can have just one menu bar. Consequently, you don't explicitly define menu bars; you just define each menu item OF the window with DEFINE MENU CHOICE. The menu items display in the order you define them on a bar attached to the top border. Use DEFINE PULLDOWN to define a pull-down menu and DEFINE PULLDOWN CHOICE to define each item on the pull-down menu. Use ON MENU CHOICE to attach a pulldown menu to a menu bar item. Menu bars and pulldown menus become active, along with any other controls in the window, when you read the window. DEFINE MENU CHOICE mFile OF MyWin PROMPT "File" DEFINE MENU CHOICE mEdit OF MyWin PROMPT "Edit" .... DEFINE PULLDOWN pFile DEFINE PULLDOWN CHOICE pFile1 OF pFile PROMPT "Delete" DEFINE PULLDOWN CHOICE pFile2 OF pFile PROMPT "Recall" .... ON MENU CHOICE mFile OF MyWin ACTIVATE PULLDOWN pFile .... A speedbar is a row or column of related buttons that cause an action when pressed. In Windows applications, speedbars typically duplicate menu items, offering a quick way to cause an action without having to find it on a menu. For instance, Print can be an item on the File menu and also a button on the speedbar. Use DEFINE SPEEDBAR to create a speedbar definition and DEFINE SPEEDBUTTON to define each button on the speedbar. DEFINE SPEEDBUTTON has an EXECUTE option for specifying a command or subroutine to run when the button is pressed. Note: In this Alpha release, speedbars are not yet implemented. A pushbutton is a single button that causes an action when pressed. For instance, an OK pushbutton can save and exit from a window, and a Cancel pushbutton can exit without saving. Typically, pushbuttons confirm an entry or selection made in other controls. Use DEFINE PUSHBUTTON to define pushbuttons. In Windows applications, if a window has a menu, the menu typically offers an item for exiting the window. If a window has no menu, push buttons (such as Cancel or Ok) typically exit the window. A window should not contain both push buttons and menu items for exiting. Step 3 - Assign actions ----------------------- You can assign actions to pull-down menu items, push buttons, and to the window itself. Assigning actions to menus is similar to dBASE IV. To assign actions to push buttons and windows, you need to understand a new concept of window selection. Pulldown Menus Assign actions to pull-down menus with ON SELECTION PULLDOWN. Typically, ON SELECTION PULLDOWN calls a procedure that uses CHOICE() or PROMPT() to determine which item was selected, then branches to other commands based on the selection. Window Selection Selecting a window accepts changes made to the window's controls, much like Ctrl-W accepts values in dBASE DOS. A user selects a window by: - Clicking on a pushbutton - Pressing Enter when the cursor is in the window (See Enter key behavior in Step 4 for details.) - Pressing Ctrl-Enter A user exits a window without selecting it by: - Pressing Esc. You can prevent this with the NoEscape option of OPEN WINDOW, or passing a logical escape parameter to READMODAL(). - Double-clicking in the window's control box, or selecting Close from the control menu, if the window has a control box. - Pressing Ctrl-F4. Use ON SELECTION WINDOW to specify a procedure to execute when a window is selected. Typically, if a window contains one or more push buttons, the ON SELECTION WINDOW procedure uses ACTIVECONTROL() to determine which push button was pressed, then branches to other commands. Push buttons differ from other controls in that one push button is always chosen when the user selects the window. The chosen push button is either the one the user presses, or the default push button if none is pressed. Trapping keystrokes You can assign actions to keystrokes, using the familiar dBASE DOS command, ON KEY. However, certain keystroke combinations are common to all Windows applications. For instance, Alt-F4 closes the active window or dialog box. Your application should not map other behavior to these reserved key combinations. Step 4 - Read the window ------------------------ Bladerunner provides two methods for reading windows: READMODAL() and OPEN WINDOW. READMODAL() Use READMODAL( ) to stop program execution and read a window. READMODAL() performs a "modal" read of a window. That is, it activates the window with exclusive focus -- the user cannot change focus to another window without first exiting the READMODAL() window. You can use READMODAL() in a command line the same way you would use other fuctions, like GETFILE(), that display a dialog box and return a value. By default, READMODAL() returns the name of the control that has focus when the user selects the window. (See "Window Selection" in Step 3.) You can query the READMODAL() return value to determine the user's action. DEFINE WINDOW Alert FROM 5,5 to 20,65 OF DESKTOP DEFINE PUSHBUTTON Ok OF Alert at 2,2 PROMPT "OK" DEFINE PUSHBUTTON Cancel OF Alert at 2,14 PROMPT "Cancel" ReturnVal = ReadModal("Alert") IF ReturnVal = "OK" DO OKProc ELSE DO CancelProc ENDIF You can specify your own return value for READMODAL() using the WITH option of CLOSE WINDOW. This makes it easy to re-use your window and dialog box definitions. The following example displays the Alert window and returns "My return value." DEFINE WINDOW Alert FROM 5,5 to 20,65 OF DESKTOP DEFINE PUSHBUTTON Ok OF Alert at 2,2 PROMPT "OK" DEFINE PUSHBUTTON Cancel OF Alert at 2,14 PROMPT "Cancel" ON SELECTION WINDOW Alert CLOSE WINDOW WITH "My return value" ReturnVal = ReadModal("Alert") You can nest READMODAL() statements. For instance, a window displayed with READMODAL() can contain a push button that launches another READMODAL() window. In this case, the user must exit the second window, then the first window, before switching focus to another window. OPEN WINDOW OPEN WINDOW performs a "non-modal" read of a window; the user can switch focus to another window without exiting the current window. Choose OPEN WINDOW over READMODAL() when you want to have more than one window active simultaneously. Also, unlike READMODAL(), OPEN WINDOW does not stop program execution. This allows you to execute more OPEN WINDOW commands, or any other code, after reading a window. While OPEN WINDOW represents a break from traditional dBASE coding techniques, where READ commands temporarily halt execution, it allows you to design true event-driven applications. A typical event-driven application simply sets up the environment, and creates and displays its windows. Further execution is determined by the user. Having execution continue after OPEN WINDOW, raises certain issues your program needs to handle. Here are the issues with our current recommendations (better solutions should follow this Alpha release -- your suggestions are encouraged!): - As always occurs in dBASE, non-PUBLIC memory variables are released when the program they are declared in finishes executing. So, if a program creates a window, assigns some memory variables, reads the window with OPEN WINDOW, then ends, the variables are released even though the window is now active. To avoid releasing these variables, you can declare them PUBLIC. - If you define controls that are based on fields in a table (for example, a GET or ENTRYFIELD using a field name), then read the window with OPEN WINDOW, then close the table, the controls will not work because the table is no longer in use. Make sure you don't close the table until the window is closed. - As always occurs in dBASE, functions and procedures declared in a program are unavailable when the program finishes executing. So, if a program contains function declarations, assigns these functions to event handlers such as a VALID or OnClosed option, then reads the window with OPEN WINDOW, the functions are not available when the program ends, even though the window may still be active. To make sure all subroutines attached to event handlers remain available, declare them in a common procedure file that always stays open. The following example shows how you can use OPEN WINDOW to read two windows. The user can launch Calc.prg to activate the Calculator, then launch Invoice.prg to activate the Invoice window. Both windows are then available, and the user can switch focus between them. * Calc.prg - a calculator DEFINE WINDOW Calculator FROM 10,2 * DEFINE controls OF Calulator .... OPEN WINDOW Calculator && Reads Calculator * More code here, if necessary .... RETURN * Invoice.prg - entry window for an invoice DEFINE WINDOW Invoice FROM 1,1 * DEFINE controls OF Invoice .... OPEN WINDOW Invoice && Reads Invoice * More code here, if necessary .... RETURN Enter key behavior One of the differences between dBASE DOS and Windows applications is the behavior of the Enter key. In dBASE DOS, pressing the Enter key moves the cursor to the next GET; pressing Enter on the last GET terminates the READ. In Windows applications, the Tab key typically moves to the next control. Pressing Enter accepts the controls much like Ctrl-W does in dBASE DOS. In Bladerunner, pressing Enter when the cursor is in the window body, but not in an editor object, accepts and exits the window. Tab and Shift-Tab move the cursor to the next and previous controls respectively. While the Windows Enter key behavior is preferable in a Windows application, you might want to retain the DOS behavior to maintain consistency with your DOS applications. Bladerunner provides SET CUAENTER to specify the behavior of the Enter key. SET CUAENTER OFF causes the Enter key to behave as in dBASE DOS; SET CUAENTER ON follows the Windows behavior. Ctrl-Enter accepts and exits and window regardless of the SET CUAENTER setting. Upgrading dBASE DOS screen forms -------------------------------- The Bladerunner UI language extensions replace the familiar dBASE DOS @...SAY/GET commands with the new DEFINE commands, DEFINE TEXT and DEFINE ENTRYFIELD. But that doesn't mean you have to stop using @...SAY/GET. To help you upgrade your dBASE DOS screen forms, Bladerunner provides two new commands, SET WINDOW TO and SET WINDOW ON. These commands direct @...SAY/GET output to . You can then define new controls for without re-writing your @...SAY/GETs. Follow these steps: 1.If your screen form is displayed in a window, consider adding some of the Bladerunner options to your DEFINE WINDOW command, such as DLGFRAME or AUTOSIZE. Otherwise, define a new window. 2.Replace the ACTIVATE WINDOW command with SET WINDOW TO and SET WINDOW ON. 3.Define any new controls you want to add to your form. 4.If you activate your form with SET FORMAT TO, remove that line. Instead, run the format file like a program, as DO . 5.Replace the READ command with READMODAL(). Here is a sample dBASE DOS screen form (Assume an open table contains the fields, FName, LName, and Color): DEFINE WINDOW SelColor FROM 1,1 to 12,50 ACTIVATE WINDOW SelColor @ 2,10 SAY "First Name" GET FName @ 4,10 SAY "Last Name" GET LName @ 6,10 SAY "Color choice" GET Color READ Here is the same screen form upgraded to Bladerunner: DEFINE WINDOW SelColor FROM 1,1 AUTOSIZE SET WINDOW TO SelColor SET WINDOW ON @ 2,10 SAY "First Name" GET FName @ 4,10 SAY "Last Name GET LName DEFINE RADIOBUTTON RadioClr WITH Color OF SelColor AT 6,23; PROMPT "Red" PICK "R", "Green" PICK "G", "Blue" PICK "B"; HORIZONTAL READMODAL("SelColor") ------------------------> EOF: UI_exten.TXT <--------------------